#ifndef _SEGMENT_CACHE_H_
#define _SEGMENT_CACHE_H_

#include "buffer.h"
#include "util.h"
#include "DCMInterface.h"
#include "Segment.h"
#include "timeutil.h"



#define INVALID_OFFSET        0xffffffff
#define CACHE_TAG             "cachinfo"
#define INFO_FILE_PATTERN     "*.info"
#define DATA_FILE_PATTERN     "*.data"
#define DEFAULT_USER          "Unknown"
#define DEFAULT_MAX_SEGMENT_ENTRY    10
#define DEFAULT_INDEX_TABLE_SIZE     4096
#define DEFAULT_CACHE_SIZE           (1024*1024*256) //256MB Cache size
#define DEFAULT_VOD_SEGSIZE          32768
#define AUTO_SAVE_DATASIZE			 (1024*1024)	// 1MB


class tUnAlignedBuffer : public tStreamBuffer
{
public:
	unsigned int BaseOffset;
	unsigned int OffsetNow;
};

class tUnAlignedBufferNode : public tUnAlignedBuffer
{
public:
	tUnAlignedBufferNode * m_pNext;

	tUnAlignedBufferNode(): m_pNext(NULL)
	{
	}
};

class tReadOperation
{
public:
	unsigned int m_Offset;
	unsigned int m_BufferLength;
	HANDLE m_hEvent;

	tReadOperation(): m_Offset(0), m_BufferLength(0)
	{
		m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	}

	void ReleaseOperation()
	{
		SetEvent(m_hEvent);
		CloseHandle(m_hEvent);
	}

	~tReadOperation()
	{
		ReleaseOperation();
	}
	

};

class tUnAlignedBufferChain
{
	tUnAlignedBufferNode *	m_pHead;
	unsigned int			m_SegmentUnitSize;
	unsigned int			m_LastSegmentIndex;
	unsigned int			m_LastSegmentSize;
	bool					m_DetectedLast;
public:
	void SetSegmentUnitSize(unsigned int Size);
	void SetLastSegment(unsigned int Index, unsigned int Size);
	eResult InsertBuffer(tUnAlignedBuffer * pBuffer);
	tUnAlignedBufferNode * GetHead();
	void PopHead();
	eResult FindCompletedSegment(std::vector<tSegment*> &refSegmentList);

	tUnAlignedBufferChain():m_pHead(NULL), m_SegmentUnitSize(0),
		m_LastSegmentIndex(0), m_LastSegmentSize(0), m_DetectedLast(false)
	{
	}
	~tUnAlignedBufferChain();

private:
	eResult InsertBuffer2(tUnAlignedBuffer * pBuffer);
	eResult SearchOffsetPosition(unsigned int OffsetNow, tUnAlignedBufferNode ** ppNode);
	tUnAlignedBufferNode * MergeToSegment(tUnAlignedBufferNode * pNode, tUnAlignedBufferNode * pPrev, tSegment * pSegment);
};

class tCachedSegment : public tSegment
{
public:
	int HitCount;
	tCachedSegment(): HitCount(0)
	{}
	tCachedSegment(const tCachedSegment& seg);
	tCachedSegment(const tSegment& seg);
	void AddHitCount()
	{
		HitCount++;
	}
	tCachedSegment& operator=(const tCachedSegment& seg);
	tCachedSegment& operator=(const tSegment& seg);
};

class tSegmentCache
{
public:
	virtual eResult ReadSegment(UINT64 Index, tSegment& Segment) = 0;
	virtual eResult WriteSegment(UINT64 Index, const tSegment& Segment) = 0;
	virtual eResult IsSegmentExist(UINT64 Index) = 0;
	virtual unsigned int GetCachedCount() = 0;
};


class sum_memcache : public std::unary_function<std::pair<const UINT64, tCachedSegment>, unsigned int>
{
public:
	unsigned int m_CachedSize;
	sum_memcache():m_CachedSize(0){}
	unsigned int operator()(std::pair<const UINT64, tCachedSegment>& ele)
	{
		m_CachedSize += (unsigned int)ele.second.Size();
		return m_CachedSize;
	}
};

class tMemSegmentCache : public tSegmentCache
{
protected:
	std::map<UINT64, tCachedSegment> SegmentMap;
	unsigned int MaxEntry;

public:
	tMemSegmentCache();
	~tMemSegmentCache();

	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
	eResult IsSegmentExist(UINT64 Index);
	unsigned int GetCachedCount();
	unsigned int GetCachedSize();
	unsigned int FlushToDiskCache(tSegmentCache * pDiskCache);
};

class tReadMemSegmentCache : public tMemSegmentCache
{
public:
	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
};

class tWriteMemSegmentCache : public tMemSegmentCache
{
public:
	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
	
};

class tSegmentIndexTable
{		
	unsigned int IndexRange;
	unsigned int * IndexTable;
public:
	tSegmentIndexTable();
	~tSegmentIndexTable();

	eResult SetIndex(UINT64 Index, unsigned int Offset);
	eResult GetIndexValue(UINT64 Index, unsigned int & Offset);
	eResult AssignIndexRange(unsigned int * pIndexTable, unsigned int aIndexRange);
	unsigned int * GetIndexTable(unsigned int & outIndexRange);
	void TruncateIndexTable(unsigned int uMaxOffset);
};

class tDiskSegmentCache :	public tSegmentCache
{
	unsigned int SegmentUnitSize;
	UINT64 FinalIndex;
	unsigned int FinalSegmentSize;
	unsigned int AppendPos;

	tSegmentIndexTable* pIndexTable;
	FILE * hCacheFile;

public:
	unsigned int m_CachedDataSize;
	unsigned int m_CachedSegmentCount;

	tDiskSegmentCache();
	~tDiskSegmentCache();

	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
	eResult IsSegmentExist(UINT64 Index);
	unsigned int GetCachedCount();

	eResult OpenCacheFile(const char * filename);
	eResult CreateCacheFile(const char * filename);
	eResult CloseCacheFile();
	unsigned int GetDataSize();
	void FlushFile();
	void InitCacheInfo(int aUnitSize, tSegmentIndexTable * pSIT, unsigned int SegCount, unsigned int CachedSize);
	void SetFinalSegment(UINT64 aFinalIndex, int aFinalSegSize);
};

class tContentCacheInfo
{
public:
	tInt128 ContentID;
	unsigned int TotalSize;
	unsigned int SegmentUnitSize;
	UINT64 FinalIndex;
	unsigned int FinalSegmentSize;
	int Usage;
	UINT64 LatestIndex;
	unsigned int SegCountOnDisk;
	
	unsigned int CacheDataSize;
	std::string m_strCacheInfoFile;

	tSegmentIndexTable IndexTable;

	tContentCacheInfo();

	eResult LoadFromFile(const char * filename, bool bOnlyHeader = false);
	eResult WriteToFile(const char * filename);
	int GetHeaderLength();

};

#ifndef _linux_
class tContentCacheItem :	public tSegmentCache,
							public tBaseInterface,
							public IDataCacheItem,
							public ICacheBufferLeak
{
	HANDLE							hMutex;
	tReadMemSegmentCache			MemoryReadCache;
	tWriteMemSegmentCache			MemoryWriteCache;
	tDiskSegmentCache				DiskCache;
	char							InfoFileName[MAX_PATH];
	char							DataFileName[MAX_PATH];
	tIndexRange						IndexRange;					// Range of received data.
	unsigned int					m_uTruncateSegCount;
	bool							bNoDiskCache;
	unsigned int					m_FreshDataSize;
	unsigned int					m_FreshReceiveDataSize;		// Use to determine the autosave.
	bool							m_bModified;
	tUnAlignedBufferChain			m_FragmentChain;			// For Alignment of data input.
	std::deque<tReadOperation*>		m_ReadQueue;				// For pending read operation.
	tStreamBuffer					m_ExpressSegment;			// The first segment with index = 0.
	HANDLE							m_ExpressLock;				// Lock for the segment.
	bool							m_DeleteMark;

public:
	tContentCacheInfo CacheItemInfo;

public:
	tContentCacheItem();
	~tContentCacheItem();

	BASEINTERFACE_METHODS

	// tSegmentCache
	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
	eResult IsSegmentExist(UINT64 Index);
	unsigned int GetCachedCount();

	// IDataCacheItem 
	tInt128 GetContentID();
	eResult ReadData(char * pBuffer, unsigned int Offset, unsigned int BufferLength, 
					unsigned int * pReadCount, bool bWait, unsigned int Timeout, HANDLE hBreakEvent = 0);
	eResult WriteData(const char * pBuffer, unsigned int Offset, unsigned int BufferLength, bool bFinal);
	eResult WriteExpressData(const char * pBuffer, unsigned int Offset, unsigned int BufferLength);
	unsigned int GetSegmentUnitSize();
	//eResult GetSegment(UINT64 Index, char * pSegmentData, unsigned int &SegmentSize);
	//eResult SetSegment(UINT64 Index, const char * pSegmentData, unsigned int SegmentSize);
	eResult GetSegment(UINT64 Index, tSegment & Seg);
	eResult FreeSegment(tSegment & Seg);
	eResult SetSegment(UINT64 Index, const tSegment& Seg);
	eResult DetectSegment(UINT64 Index);
	eResult SetFinalSegment(UINT64 Index, unsigned int Size);
	UINT64 GetFinalSegment(unsigned int * pFinialSegSize);
	void GetIndexRange(tIndexRange * pIR);
	UINT64 GetLatestSegmentIndex();
	unsigned int GetTotalSize();
	void SetTotalSize(unsigned int size);
	void Hit();
	unsigned int GetCachedDataSize();


	// ICacheBufferLeak
	virtual unsigned int GetFreshDatasize();
	virtual void LeakFreshData(unsigned int size);
	virtual void FlushFreshData();

	// Used by internal manager.
	unsigned int GetDataSize();
	eResult InitCacheItem(tInt128 ContID, int SegUnitSize,
							const char * infofile, const char * datafile);
	void UpdateCacheUsage();
	eResult LoadFromFile(const char * infofile, const char * datafile);
	unsigned int TruncateCache(unsigned int uSize);
	eResult SaveToFile();
	void SaveData();
	bool CanBeRelease();
	void MarkAsDelete();
	bool CanBeDelete();
private:
	void CheckPendingOperation();

	// Input data alignment functions.
	eResult AddToFragmentChain(tUnAlignedBuffer & InputBuffer);
	eResult ComposeSegmentFromFragments();
};

class tContentCacheMgr :	public tBaseInterface,
							public IDataCacheManager,
							public IMsgNotify
{
	HANDLE hMutex;
	UINT64 CacheSize;
	char CachePath[MAX_PATH];
	UINT64 MaxCacheSize;
	std::deque<tContentCacheItem*> CacheItemList;
	tTimer			m_Timer;
	unsigned int	m_TimerID_AutoSave;
	unsigned int	m_TimerID_AutoUnload;

public:
	tContentCacheMgr();
	~tContentCacheMgr();

	BASEINTERFACE_METHODS

	// Interface IDataCacheManager:
	void SetCacheSize(UINT64 Size) ;
	void SetCachePath(const char * Path);
	eResult InitCacheMgr(UINT64 Size, const char * Path);
	eResult UnInitCacheMgr();
	IDataCacheItem* CreateCacheItem(tInt128 ContentID, const char * strWho);
	IDataCacheItem * GetCacheItem(tInt128 ContentID, const char * strWho);
	eResult DeleteCacheItem(tInt128 ContentID);
	eResult GetCachedItemInfo(tCacheItemInfo * pCacheItemInfoList, unsigned int * ListLength);

	//IMsgNofity
	int NotifyMessage(int NotifyID, long Data1, long Data2, void * DataPtr, int DataSize);


private:
	tContentCacheItem* LoadCacheItem(tInt128 ContentID);
	eResult UnloadCacheItem(tInt128 ContentID);

	eResult DetermineMaxSize();
	eResult CheckCacheSize();
	eResult LoadAllCacheItems();
	eResult MaintainCacheSpace();
	eResult UnloadAllCacheItems();
	//eResult UpdateCacheStatus(tContentCacheItem * pItem);
	//eResult UpdateCacheUsage(UINT64 ContentID);
	unsigned int TruncateCacheItem(tContentCacheItem * pCacheItem, unsigned int uSize);
	eResult DeleteCacheItemLater(tCacheItemInfo * pItem);
	tContentCacheItem * FindCacheItem(tInt128 ContentID);
	eResult GetInfoFileName(tInt128 ContentID, char * path);
	eResult GetDataFileName(tInt128 ContentID, char * path);
	//eResult LoadCacheStatusFromFile(char * path, tCacheStatus * Status);
	void GetFullFileName(const char * filename, char * fullpath);
	eResult DataName2InfoName(char * in, char * out);
	eResult InfoName2DataName(char * in, char * out);
};
#endif //_linux_

//====================================================================
// The following codes defines a cache in memory for TV(live stream)
//====================================================================
class tLiveSegmentCache : public tSegmentCache
{
protected:
	unsigned int MaxQueueLength;
	std::deque<tCachedSegment> SegmentQueue;
public:	
	tLiveSegmentCache();
	virtual ~tLiveSegmentCache();
	
	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);
	eResult IsSegmentExist(UINT64 Index);
	unsigned int GetCachedCount();

};

//===================================================================
// The following codes defines a TV Stream Cache itme for receiving
// and buffering TV live stream.
//===================================================================
class tTVItem : public tLiveSegmentCache, 
				public tBaseInterface,
				public ITVCacheItem,
				public ITVServiceCacheInfo
{
	tMutex ResLock;
	std::deque<char*> m_UserReference;

public:
	//int Speed;
	tInt128 ContentID;
	SOCKET sock;
	unsigned int m_uHistorySize;
public:
	tTVItem();
	~tTVItem();

	eResult ReadSegment(UINT64 Index, tSegment& Segment);
	eResult WriteSegment(UINT64 Index, const tSegment& Segment);

    void SetContentID(tInt128 ContentID);

	BASEINTERFACE_METHODS

	// IDataCacheItem 
	tInt128 GetContentID();
	unsigned int GetSegmentUnitSize();
	//eResult GetSegment(UINT64 Index, char * pSegmentData, unsigned int &SegmentSize);
	//eResult SetSegment(UINT64 Index, const char * pSegmentData, unsigned int SegmentSize);
	eResult GetSegment(UINT64 Index, tSegment & Seg);
	eResult SetSegment(UINT64 Index, const tSegment& Seg);
	eResult DetectSegment(UINT64 Index);
	void GetIndexRange(tIndexRange * pIR);
    void Reset();

	//ITVServiceCacheInfo
	virtual unsigned int GetHistorySize();
	virtual int GetRange(UINT64 *uHead, UINT64 *uTail);


};

//===================================================================
// Define the TV item manager.
//===================================================================
typedef std::map<tInt128, tTVItem*> MAP_TV;

class tTVCacheManager : public tBaseInterface,
						public ITVCacheManager
{
	tMutex ResLock;
	MAP_TV m_mapTVItems;
public:
	tTVCacheManager();
	~tTVCacheManager();

	BASEINTERFACE_METHODS

	// ITVCacheManager Interface.
	ITVCacheItem * GetTVItem(tInt128 iContentID, const char * strWho);
	ITVCacheItem * CreateTVItem(tInt128 iContentID, const char * strWho);
	eResult RemoveTVItem(tInt128 iContentID, const char * strWho);
};

#endif
